﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

namespace TTGame {

    /// <summary>
    ///
    /// bool isStoredObj: true，创建的缓存对象只是为了填充缓存池，请把他设置隐藏。
    /// </summary>
public delegate U CreateCacheObj<U>(bool isStoredObj);

public delegate void InitCacheObj<U>(U obj, bool isStoredObj);

/*
 * 缓存池。创建的对象存在这里。不使用时放回池中，不释放，等待下次使用，减少对象新建次数。
 *
 * Notice:
 * 从缓存池中获取的对象需要初始化。
 *
 * 效率：使用Message对象测试过。除Recycle方法外，创建部分比new Message平均时间削微的快一丢丢，1ms以内。
 * Message只有一个成员变量，如果是较大的类，则效果应该会更好。
 *
 * 和ObjectRecycle的区别，可用于非Object对象，
 * ObjectRecycle只能用于Prefab的创建。
 */
public class PoolCacher<T> where T : new() {
    //Dictionary<T, bool> OnUsedPool = new Dictionary<T, bool>();
    List<T> OnUsedPoolKeys = new List<T>();
    List<bool> OnUsedPoolValues = new List<bool>();
    Stack<T> NoUsedPool = new Stack<T>();
    CreateCacheObj<T> m_createFunc;
    InitCacheObj<T> m_initFunc;
    int m_poolCapacity = 0;

    public int Capacity {
        set {
            if(m_poolCapacity == value) {
                return;
            }
            m_poolCapacity = value;
            if(m_poolCapacity < 0) {
                m_poolCapacity = 0;
            }
            resetCapacity();
        }
        get {
            return m_poolCapacity;
        }
    }

    /// <summary>
    /// 使用默认的new初始化。
    /// 但是强制需要设置初始化方法，为了防止忘记初始化。
    /// </summary>
    /// <param name="initFunc">使用默认的创建方法，即new。新对象返回前，会调用该方法初始化。</param>
    public PoolCacher(InitCacheObj<T> initFunc) {
        m_createFunc = null;
        m_initFunc = initFunc;
    }
    /// <summary>
    /// 使用自定义的create方法，请在创建对象后中记得初始化它。
    /// </summary>
    /// <param name="createObjFunc">提供自定义的创建的方法</param>
    public PoolCacher(CreateCacheObj<T> createObjFunc) {
        m_createFunc = createObjFunc;
        m_initFunc = null;
    }

    public List<T> getUsingList() {
        return OnUsedPoolKeys;
    }

    public int getPoolCount() {
        return OnUsedPoolKeys.Count + NoUsedPool.Count;
    }

    T obtainFromNoUsedPool() {
        T obj = default(T);
        while(obj == null && NoUsedPool.Count > 0) {
            obj = NoUsedPool.Pop();
#if UNITY_EDITOR
            if(obj == null) {
                //DebugUtil.LogWarning("[PoolCacher] Get null obj from pool. 对象使用结束，应当调用Recycle，而不是释放" + DumpStr());
            }
#endif
        }
        if(obj != null) {
            if(m_initFunc != null)
                m_initFunc(obj, false);
        }
        return obj;
    }

    T createNewObj(bool isStoredObj = false) {
        T obj;
        if(m_createFunc == null) {
            obj = new T();
            if(m_initFunc != null)
                m_initFunc(obj, isStoredObj);
        } else {
            obj = m_createFunc(isStoredObj);
        }
        return obj;
    }


    public int OnUsedObjCount()
    {
       return OnUsedPoolKeys.Count;
    }

    public T OnUsedObjByIndex(int idx)
    {
        if (idx >= OnUsedPoolKeys.Count)
        {
            Debug.LogError("Error Index");
        }
        return OnUsedPoolKeys[idx];
    }

    public bool OnUsedValuesByIndex(int idx)
    {
        if (idx >= OnUsedPoolValues.Count)
        {
            Debug.LogError("Error Index");
        }
        return OnUsedPoolValues[idx];
    }

    /*
     * 获取一个对象。优先从缓存池中获取，否则新建一个
     * 注：得到新对象之后，请确保对象已充分初始化。
     */
    public T ObtainObj() {
        T obj = obtainFromNoUsedPool();
        if(obj == null) {
            obj = createNewObj();
        }
        if(OnUsedPoolKeys.Contains(obj) == false) {
            OnUsedPoolKeys.Add(obj);
            OnUsedPoolValues.Add(true);
        } else {
            //DebugUtil.LogErrorFormat("[PoolCacher] Duplicate Key In Dict. PoolDump:[{0}]", DumpStr());
        }
        return obj;
    }

    /*将对象放回缓存池。注：对象放回缓存池之前，一定一定要清空。不然可能出现引用残留，无法释放的问题。*/
    public bool BackToCache(T obj) {
        int index = OnUsedPoolKeys.IndexOf(obj);
#if UNITY_EDITOR
        if (typeof(T) == typeof(AudioSource))
        {
            //DebugUtil.Log("[PoolCacher] RecycleObj. info: " + obj + "   "+obj.GetHashCode());
        }
#endif

        if(index != -1) {
            OnUsedPoolKeys.RemoveAt(index);
            OnUsedPoolValues.RemoveAt(index);
            NoUsedPool.Push(obj);
            return true;
        } else {
            //mark:wtr 不是从池中创建的对象，却放回到了池中。（代码逻辑有问题或者找王挺然问下。）
            //DebugUtil.LogError("[PoolCacher] Back Obj is not Create By Pool." + obj + "   "+obj.GetHashCode());
            return false;
        }

    }
    //少了添加，多的不删。目前删除元素没有回调。
    void resetCapacity() {
        int count = getPoolCount();
        int addCount = m_poolCapacity - count;
        for(int i = 0; i < addCount; i ++){
            NoUsedPool.Push(createNewObj(true));
        }
    }

    public void ClearPool() {
        OnUsedPoolKeys.Clear();
        OnUsedPoolValues.Clear();
        NoUsedPool.Clear();
        resetCapacity();
    }

    public bool IsAllCacheBack(){
        return OnUsedPoolKeys.Count == 0;
    }

    public string DumpStr() {
        return string.Format("[PoolCacher] type{0} using count:{1} free count:{2}", typeof(T).ToString(), OnUsedPoolKeys.Count, NoUsedPool.Count);
    }
}
}